package tinybase.pf;

import java.io.*;
import java.nio.ByteBuffer;
import java.util.*;

import tinybase.basic.BytePointer;
import tinybase.basic.IntegerWrapper;
import tinybase.basic.RC;



public class PF_Manager {

    private PF_BufferMgr pBufferMgr;
    public PF_BufferMgr getpBufferMgr() {
		return pBufferMgr;
	}

	public PF_FileHandle pfFh;

    //Constructor. Initialise instances variables
    public PF_Manager(){

        pBufferMgr = new PF_BufferMgr(PF_Internal.PF_BUFFER_SIZE);

    }

    // createFile
    //
    // Desc: This methode will create a file named filename  
    // In:   filename - The name of the file to create
    // Ret:  Returns PF error code
    public RC createFile(String filename){

        //Create a new file named filename
        File f = new File(filename);
//        System.out.println(filename);
        if (f.exists())
            return RC.PF_UNIX;

        try {

            f.createNewFile();

            RandomAccessFile ioStream = new RandomAccessFile(f, "rws");

            //Initialize the file header
            int numPages = 0;
            int firstFree = PF_Internal.PF_PAGE_LIST_END;

            ioStream.setLength(Pf.PF_PAGE_SIZE);
            ioStream.seek(0);
            ioStream.writeInt(firstFree);
            ioStream.writeInt(numPages);
            ioStream.close();

        } catch (FileNotFoundException e) {

            return RC.PF_UNIX;

        }catch (IOException e) {

            return RC.PF_UNIX;
        }

        return RC.SUCCESS;
    }

    // openFile
    //
    // Desc: This methode will open the file named filename and pass it to the  
    // a PF_FileHandle objet.
    // In:   filename - The name of the file to open
    //       fileHandle - the PF_Filehandle object corresponding to the file
    // Ret:  Returns PF error code
    public RC openFile(String filename, PF_FileHandle fileHandle) {

    	if (fileHandle.isFileOpen())
    		return RC.PF_FILEOPEN;
    	
        //Make sure the file exist
        File f = new File(filename);
        
		if (!f.exists()) {
			return RC.PF_UNIX;
		} else {

			try {
				RandomAccessFile rf = new RandomAccessFile(f, "rws");
				rf.seek(0);
				byte[] buf = new byte[8];
				rf.read(buf);
				ByteBuffer bbuf = ByteBuffer.wrap(buf);
				int firstFree = bbuf.getInt(0);
				int numPages = bbuf.getInt(4);
				fileHandle.getFileHeader().setFirstFree(firstFree);
				fileHandle.getFileHeader().setNumPages(numPages);

				// Mark the file open
				fileHandle.setRandomAccessFile(rf);// zliu
				fileHandle.setFileOpen(true);
				fileHandle.setHdrChanged(true);
				fileHandle.setBufferMgr(pBufferMgr);

				System.out.println("Openfile success !");

				return RC.SUCCESS;
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(1);
				return RC.PF_HDRREAD;
			}
		}
	}


    // closeFile
    //
    // Desc: The methode will close the file corresponding to the 
    // PF_FileHandle objet.
    // In:   The PF_Filehandle object corresponding to the file to close
    // Ret:  Returns PF error code
    public RC closeFile(PF_FileHandle fileHandle) {

    	if (!fileHandle.isFileOpen())
    		return RC.PF_CLOSEDFILE;
    	
        //Flush all the pages from the buffer pool, corresponding to the fileHandle object
        fileHandle.flushPages();

        //Get the file descriptor object        
        if (!fileHandle.isFileOpen())
        	return RC.PF_CLOSEDFILE;

        // Close the file
        try {
        	fileHandle.setFileOpen(false);
        	fileHandle.getRandomAccessFile().close();
        } catch (IOException e) {
            return RC.PF_EOF;
        }

        //Mark the file as closed
        

        return RC.SUCCESS;
    }


    // destroyFile
    //
    // Desc: Destroy the file named filename.
    // In:   The name of the file to destroy
    // Ret:  Returns PF error code
    public RC destroyFile(String filename) {

        File f = new File(filename);

        //Make sure the file exist
        if (!f.exists())
            return RC.PF_UNIX;

        //Delete the file
        if (!f.delete())
            return RC.PF_UNIX;

        return RC.SUCCESS;

    }



    // ClearBuffer
    //
    // Desc: Remove all entries from the buffer manager.
    //       This routine will be called via the system command and is only
    //       really useful if the user wants to run some performance
    //       comparison starting with an clean buffer.
    // In:   Nothing
    // Out:  Nothing
    // Ret:  Returns the result of PF_BufferMgr::ClearBuffer
    //       It is a code: 0 for success, something else for a PF error.
    //
    public RC clearBuffer()
    {
        return pBufferMgr.clearBuffer();
    }

    // PrintBuffer
    //
    // Desc: Display all of the pages within the buffer.
    //       This routine will be called via the system command.
    // In:   Nothing
    // Out:  Nothing
    // Ret:  Returns the result of PF_BufferMgr::PrintBuffer
    //       It is a code: 0 for success, something else for a PF error.

    public RC printBuffer()
    {
        return pBufferMgr.printBuffer();
    }


    // ResizeBuffer
    //
    // Desc: Resizes the buffer manager to the size passed in.
    //       This routine will be called via the system command.
    // In:   The new buffer size
    // Out:  Nothing
    // Ret:  Returns the result of PF_BufferMgr.ResizeBuffer
    //       It is a code: 0 for success, PF_TOOSMALL when iNewSize
    //       would be too small.

    public RC resizeBuffer(int iNewSize)
    {
        return pBufferMgr.resizeBuffer(iNewSize);
    }

    //------------------------------------------------------------------------------
    // Three Methods for manipulating raw memory buffers.  These memory
    // locations are handled by the buffer manager, but are not
    // associated with a particular file.  These should be used if you
    // want memory that is bounded by the size of the buffer pool.
    //
    // The PF_Manager just passes the calls down to the Buffer manager.
    //------------------------------------------------------------------------------


    public RC getBlockSize(IntegerWrapper length)
    {
        return pBufferMgr.getBlockSize(length);
    }

    //allocate a new block to the file
    public RC allocateBlock(BytePointer buffer)
    {
        return pBufferMgr.allocateBlock(buffer);
    }

    //Delete a block from the file
    public RC disposeBlock(byte[] buffer)
    {
        return pBufferMgr.disposeBlock(buffer);
    }

}
